package com.shi.yibanjia.service.impl;

import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.shi.yibanjia.entity.dto.*;
import com.shi.yibanjia.entity.po.Account;
import com.shi.yibanjia.entity.po.Account2Role;
import com.shi.yibanjia.entity.vo.AccountVO;
import com.shi.yibanjia.exception.BusinessException;
import com.shi.yibanjia.mapper.AccountMapper;
import com.shi.yibanjia.service.AccountService;
import com.shi.yibanjia.util.PasswordUtil;
import com.shi.yibanjia.util.RedisUtil;
import com.shi.yibanjia.util.SpringUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

import static com.shi.yibanjia.constants.RedisConstant.*;


/**
 * (Account)表服务实现类
 *
 * @author makejava
 * @since 2024-05-16 17:18:00
 */
@Service("accountService")
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    /**
     * 新增数据
     *
     * @param accountAddDto 实例对象
     * @return 实例对象
     */
    @Transactional
    @Override
    public void add(AccountAddDto accountAddDto) {
        Account byUsername = lambdaQuery().eq(Account::getUsername, accountAddDto.getUsername()).one();
        if (byUsername != null) {
            throw new BusinessException("该用户已存在");
        }
        accountAddDto.setPassword(PasswordUtil.encode(accountAddDto.getPassword()));
        Account bean = BeanUtil.toBean(accountAddDto, Account.class);
        baseMapper.insert(bean);
        List<Integer> roleIds = accountAddDto.getRoleIds();
        AccountService accountService = SpringUtil.getBean(AccountService.class);
        accountService.giveRoles(roleIds, bean.getUid());
    }

    /**
     * 通过主键删除数据
     *
     * @param uid 主键
     * @return 是否成功
     */
    @Transactional
    @Override
    public void removeById(Integer uid) {
        // 1. 删除用户
        removeById(uid);
        // 2. 删除用户角色关系
        Db.lambdaUpdate(Account2Role.class)
                .eq(Account2Role::getAccountId, uid)
                .remove();
    }

    /**
     * 通过主键批量删除数据
     *
     * @param uids 主键
     * @return 是否成功
     */
    @Transactional
    @Override
    public void removeBatchByIds(List<Integer> uids) {
        if (CollectionUtils.isEmpty(uids)) {
            return;
        }
        // 1. 删除用户
        baseMapper.deleteBatchIds(uids);
        // 2. 删除用户角色关系
        Db.lambdaUpdate(Account2Role.class)
                .in(Account2Role::getAccountId, uids)
                .remove();
    }

    @Override
    public Account usernameLogin(AccountUsernameLoginDto accountUsernameLoginDto) {
        String checkCodeKey = accountUsernameLoginDto.getCheckCodeKey();
        String username = accountUsernameLoginDto.getUsername();
        String password = accountUsernameLoginDto.getPassword();
        String checkCode = accountUsernameLoginDto.getCheckCode();
        String checkCodeInCache=RedisUtil.get(checkCodeKey);
        if (checkCodeInCache==null) {
            throw new BusinessException("请刷新验证码");
        }
        boolean verified = new MathGenerator().verify(checkCodeInCache,checkCode);
        if (!verified) {
            throw new BusinessException("验证码错误");
        }
        Account account = lambdaQuery().eq(Account::getUsername, username).one();
        if (account == null) {
            throw new BusinessException("用户不存在");
        }
        if (!PasswordUtil.match(account.getPassword(), password)) {
            throw new BusinessException("密码不正确");
        }
        RedisUtil.delete(checkCodeKey);
        account.setLastLoginTime(LocalDateTime.now());
        updateById(account);
        return account;
    }

    @Override
    public List<Integer> queryRoleIdsByAccountId(Integer accountId) {
        List<Account2Role> list = Db.lambdaQuery(Account2Role.class)
                .eq(Account2Role::getAccountId, accountId)
                .list();
        return list.stream().map(item -> item.getRoleId()).toList();
    }

    @Transactional
    @Override
    public void giveRoles(List<Integer> roleIds, Integer accountId) {
        // 1. 先删除原来拥有的用户角色关系
        Db.lambdaUpdate(Account2Role.class)
                .eq(Account2Role::getAccountId, accountId)
                .remove();
        // 2. 添加新的用户角色关系
        if (CollectionUtils.isEmpty(roleIds)) {
            return;
        }
        List<Account2Role> account2Roles = roleIds.stream().map(id -> {
            Account2Role account2Role = new Account2Role();
            account2Role.setAccountId(accountId);
            account2Role.setRoleId(id);
            return account2Role;
        }).toList();
        Db.saveBatch(account2Roles);
    }

    @Override
    public Account queryByEmail(String email) {
        Account account = lambdaQuery().eq(Account::getEmail, email).one();
        return account;
    }

    @Override
    public void updateSelfPassword(AccountUpdateSelfPasswordDto updateSelfPasswordDto) {
        Integer accountId = updateSelfPasswordDto.getUid();
        String oldPassword = updateSelfPasswordDto.getOldPassword().trim();
        String newPassword = updateSelfPasswordDto.getNewPassword().trim();
        String confirmPassword = updateSelfPasswordDto.getConfirmPassword().trim();
        if (!Objects.equals(newPassword,confirmPassword)){
            throw new BusinessException("两次输入密码不一致");
        }
        Account one = lambdaQuery().eq(Account::getUid, accountId).one();
        if (one==null){
            throw new BusinessException("该用户不存在");
        }
        if (!Objects.equals(PasswordUtil.encode(oldPassword),one.getPassword())){
            throw new BusinessException("旧密码不正确");
        }
        Account account = new Account();
        account.setPassword(PasswordUtil.encode(newPassword));
        account.setUid(accountId);
        lambdaUpdate().update(account);
    }

    @Override
    public void forgetPassword(AccountForgetPasswordDto forgetPasswordDto,HttpSession session) {
        String email = forgetPasswordDto.getEmail().trim();
        String code = forgetPasswordDto.getCode().trim();
        String newPassword = forgetPasswordDto.getNewPassword().trim();
        String confirmPassword = forgetPasswordDto.getConfirmPassword().trim();
        // 1. 验证code是否正确
        String codeInCache=RedisUtil.get(EMAIL_Check_CODE_FORGET_PASSWORD+email);
        if (!code.equalsIgnoreCase(codeInCache)) {
            throw new BusinessException("邮箱验证码不正确");
        }
        // 2. 验证两次密码是否一致
        if (!newPassword.equals(confirmPassword)) {
            throw new BusinessException("两次输入的密码不一致");
        }
        // 2. 通过email查询用户
        Account account = queryByEmail(email);
        if (account == null) {
            throw new BusinessException("该邮箱号未绑定");
        }
        account.setPassword(PasswordUtil.encode(newPassword));
        updateById(account);
        RedisUtil.delete(EMAIL_Check_CODE_FORGET_PASSWORD+email);
    }

    @Override
    public void updateBindEmail(AccountBindEmailDto accountBindEmailDto,HttpSession session) {
        Integer uid = accountBindEmailDto.getUid();
        String newEmail = accountBindEmailDto.getNewEmail().trim();
        String loginPassword = accountBindEmailDto.getLoginPassword().trim();
        String checkCode = accountBindEmailDto.getCheckCode().trim();
        // 1. 校验邮箱验证码
        String emailCode=RedisUtil.get(EMAIL_CHECK_CODE_UPDATE_BIND_EMAIL+newEmail);
        if (emailCode==null){
            throw new BusinessException("验证码已过期");
        }
        if (!emailCode.equalsIgnoreCase(checkCode)){
            throw new BusinessException("验证码不正确");
        }
        // 2. 校验登录密码
        Account one = lambdaQuery().eq(Account::getUid,uid).one();
        if (!Objects.equals(one.getPassword(),PasswordUtil.encode(loginPassword))){
            throw new BusinessException("登录密码不正确");
        }
        one.setEmail(newEmail);
        updateById(one);
        RedisUtil.delete(EMAIL_CHECK_CODE_UPDATE_BIND_EMAIL+newEmail);
    }

    @Override
    public Account emailLogin(AccountEmailLoginDto accountEmailLoginDto) {
        String email = accountEmailLoginDto.getEmail().trim();
        String checkCode = accountEmailLoginDto.getCheckCode().trim();
        Account one = lambdaQuery().eq(Account::getEmail, email).one();
        if (one==null){
            throw new BusinessException("该邮箱名下没有绑定账号");
        }
        String codeInCache=RedisUtil.get(LOGIN_CHECK_CODE_EMAIL+email);
        if (codeInCache==null){
            throw new BusinessException("验证码已过期");
        }
        if (!checkCode.equalsIgnoreCase(codeInCache)){
            throw new BusinessException("验证码不正确");
        }
        RedisUtil.delete(LOGIN_CHECK_CODE_EMAIL+email);
        one.setLastLoginTime(LocalDateTime.now());
        updateById(one);
        return one;
    }

    @Override
    public List<AccountVO> listByCondition(String likeProp) {
        List<Account> list = lambdaQuery()
                .like(Account::getUsername, likeProp)
                .or()
                .like(Account::getAvatar, likeProp)
                .orderByDesc(Account::getCreateTime)
                .list();
        return Account.toAccountVOList(list);
    }
}
